Telegram Group & Telegram Channel
🖥 Задача: "Кэширование и ленивые вычисления в многопоточном окружении"

🔜 Условие:

Вам нужно реализовать декоратор @thread_safe_cached, который:

- Кэширует результат вызова функции по её аргументам (аналог functools.lru_cache, но свой).
- Если несколько потоков одновременно вызывают функцию с одинаковыми аргументами:
- Только один поток реально выполняет функцию,
- Остальные ждут, пока результат будет вычислен, и получают готовый результат.
- Кэш никогда не удаляется (неограниченный размер).

Ограничения:

- Решение должно работать для произвольных функций и аргументов (hashable).
- Нельзя использовать готовый functools.lru_cache или другие библиотеки кэширования.
- Нужно обеспечить корректную работу в многопоточной среде без гонок данных.

---

▪️ Подсказки:

- Для кэширования подойдёт dict с ключами по аргументам (`*args`, `**kwargs`).
- Для защиты доступа к кэшу понадобится threading.Lock.
- Для ожидания завершения вычисления другими потоками можно использовать threading.Event.
- Продумайте carefully: как отличить "результат уже посчитан" от "результат в процессе вычисления".

---

▪️ Что оценивается:

- Умение работать с многопоточностью в Python.
- Правильная организация кэширования.
- Чистота и лаконичность кода.
- Умение обрабатывать тонкие случаи, например: одновременные вызовы.

---

▪️ Разбор возможного решения:

Основная идея:

- Создать кэш cache: Dict[Key, Result].
- Одновременно создать словарь "ожиданий" in_progress: Dict[Key, threading.Event].
- Если кто-то начал считать значение:
- Остальные ждут Event, пока оно не будет установлено.

Пример реализации:


import threading
import functools

def thread_safe_cached(func):
cache = {}
in_progress = {}
lock = threading.Lock()

@functools.wraps(func)
def wrapper(*args, **kwargs):
key = (args, frozenset(kwargs.items()))
with lock:
if key in cache:
return cache[key]
if key not in in_progress:
in_progress[key] = threading.Event()
in_progress[key].clear()
creator = True
else:
creator = False

if creator:
try:
result = func(*args, **kwargs)
with lock:
cache[key] = result
finally:
in_progress[key].set()
with lock:
del in_progress[key]
return result
else:
in_progress[key].wait()
with lock:
return cache[key]

return wrapper


---

▪️ Пояснения к коду:

- При первом вызове для новых аргументов поток создаёт Event и начинает считать результат.
- Остальные потоки видят Event и вызывают wait(), пока первый поток не установит set().
- Как только результат посчитан, Event сигнализирует всем ждущим потокам, что данные готовы.
- Доступ к cache и in_progress защищён через lock для избежания гонок.

---

▪️ Возможные подводные камни:

- Если не удалять Event из in_progress, кэш постепенно раздуется мусором.
- Если ошибка случится внутри func, необходимо всё равно освободить Event, иначе потоки будут вечно ждать.
- Нельзя держать lock во время выполнения тяжёлой функции func, иначе все потоки будут блокироваться.

---

▪️ Вопросы на собеседовании по этой задаче:

- Как изменить реализацию, чтобы кэш имел ограничение по размеру (например, максимум 1000 элементов)?
- Как адаптировать декоратор под асинхронные функции (`async def`)?
- Что будет, если func иногда бросает исключения? Как кэшировать ошибки или не кэшировать их?
- Как изменить реализацию так, чтобы кэш удалял устаревшие данные через TTL (Time-To-Live)?

---

@pythonl
Please open Telegram to view this post
VIEW IN TELEGRAM



tg-me.com/pythonl/4782
Create:
Last Update:

🖥 Задача: "Кэширование и ленивые вычисления в многопоточном окружении"

🔜 Условие:

Вам нужно реализовать декоратор @thread_safe_cached, который:

- Кэширует результат вызова функции по её аргументам (аналог functools.lru_cache, но свой).
- Если несколько потоков одновременно вызывают функцию с одинаковыми аргументами:
- Только один поток реально выполняет функцию,
- Остальные ждут, пока результат будет вычислен, и получают готовый результат.
- Кэш никогда не удаляется (неограниченный размер).

Ограничения:

- Решение должно работать для произвольных функций и аргументов (hashable).
- Нельзя использовать готовый functools.lru_cache или другие библиотеки кэширования.
- Нужно обеспечить корректную работу в многопоточной среде без гонок данных.

---

▪️ Подсказки:

- Для кэширования подойдёт dict с ключами по аргументам (`*args`, `**kwargs`).
- Для защиты доступа к кэшу понадобится threading.Lock.
- Для ожидания завершения вычисления другими потоками можно использовать threading.Event.
- Продумайте carefully: как отличить "результат уже посчитан" от "результат в процессе вычисления".

---

▪️ Что оценивается:

- Умение работать с многопоточностью в Python.
- Правильная организация кэширования.
- Чистота и лаконичность кода.
- Умение обрабатывать тонкие случаи, например: одновременные вызовы.

---

▪️ Разбор возможного решения:

Основная идея:

- Создать кэш cache: Dict[Key, Result].
- Одновременно создать словарь "ожиданий" in_progress: Dict[Key, threading.Event].
- Если кто-то начал считать значение:
- Остальные ждут Event, пока оно не будет установлено.

Пример реализации:


import threading
import functools

def thread_safe_cached(func):
cache = {}
in_progress = {}
lock = threading.Lock()

@functools.wraps(func)
def wrapper(*args, **kwargs):
key = (args, frozenset(kwargs.items()))
with lock:
if key in cache:
return cache[key]
if key not in in_progress:
in_progress[key] = threading.Event()
in_progress[key].clear()
creator = True
else:
creator = False

if creator:
try:
result = func(*args, **kwargs)
with lock:
cache[key] = result
finally:
in_progress[key].set()
with lock:
del in_progress[key]
return result
else:
in_progress[key].wait()
with lock:
return cache[key]

return wrapper


---

▪️ Пояснения к коду:

- При первом вызове для новых аргументов поток создаёт Event и начинает считать результат.
- Остальные потоки видят Event и вызывают wait(), пока первый поток не установит set().
- Как только результат посчитан, Event сигнализирует всем ждущим потокам, что данные готовы.
- Доступ к cache и in_progress защищён через lock для избежания гонок.

---

▪️ Возможные подводные камни:

- Если не удалять Event из in_progress, кэш постепенно раздуется мусором.
- Если ошибка случится внутри func, необходимо всё равно освободить Event, иначе потоки будут вечно ждать.
- Нельзя держать lock во время выполнения тяжёлой функции func, иначе все потоки будут блокироваться.

---

▪️ Вопросы на собеседовании по этой задаче:

- Как изменить реализацию, чтобы кэш имел ограничение по размеру (например, максимум 1000 элементов)?
- Как адаптировать декоратор под асинхронные функции (`async def`)?
- Что будет, если func иногда бросает исключения? Как кэшировать ошибки или не кэшировать их?
- Как изменить реализацию так, чтобы кэш удалял устаревшие данные через TTL (Time-To-Live)?

---

@pythonl

BY Python/ django


Warning: Undefined variable $i in /var/www/tg-me/post.php on line 283

Share with your friend now:
tg-me.com/pythonl/4782

View MORE
Open in Telegram


Python django Telegram | DID YOU KNOW?

Date: |

What Is Bitcoin?

Bitcoin is a decentralized digital currency that you can buy, sell and exchange directly, without an intermediary like a bank. Bitcoin’s creator, Satoshi Nakamoto, originally described the need for “an electronic payment system based on cryptographic proof instead of trust.” Each and every Bitcoin transaction that’s ever been made exists on a public ledger accessible to everyone, making transactions hard to reverse and difficult to fake. That’s by design: Core to their decentralized nature, Bitcoins aren’t backed by the government or any issuing institution, and there’s nothing to guarantee their value besides the proof baked in the heart of the system. “The reason why it’s worth money is simply because we, as people, decided it has value—same as gold,” says Anton Mozgovoy, co-founder & CEO of digital financial service company Holyheld.

Python django from ru


Telegram Python/ django
FROM USA